#pragma once

#include <iostream>
#include <unistd.h> // 包含了：read, write等函数
#include <cstring>  // C++版本的string.h
#include <cerrno>   // C++版本的errno.h
#include <sys/wait.h>
#include <sys/types.h> // 包含了：open, mkfifo等函数
#include <sys/stat.h>  // 包含了：open, mkfifo等函数
#include <string>
#include <fcntl.h> // 包含了：open等函数
#include <vector>
#include <ctime>
#include <cstdlib> // 包含了: perror等函数
#include <cstdio>  // 包含了: perror等函数

// 创建命名管道的方法，源文件中使用int mkfifo(const char *filename, mode_t mode); 这个函数
//  mkfifo 的第一个参数指定有名管道的文件名，以及该文件所处的路径
//  mkfifo 的第二个参数指定创建的文件的权限
//  mkfifo 的返回值是-1，表明创建失败，返回值是0，表明创建成功
// 这里不用main函数的参数进行传参，直接通过定义管道文件的名称获得  命名管道
const std::string common_path = "./mypipe";
#define DefaultFd -1
#define Creator 1
#define User 2

// 操作系统给系统调用的接口是
#define Read O_RDONLY
#define Write O_WRONLY
#define BaseSize 4096

class NamedPipe
{
private:
    bool openNamedPipe(int mode)
    {
        _fd = open(_fifo_path.c_str(), mode);
        if (_fd < 0)
        {
            return false;
        }
        return true;
    }

public:
    // 构造函数的参数是创建对象时，由用户给定，
    // 所以要创建NamedPipe这个类型的对象，只需要在构造时说明
    // 1. 用户创建管道的路径
    // 2. 用户是否为有名管道的创建者
    // 构造函数的功能：
    //   1. 创建一个有名管道，指定路径和权限，如果创建失败用perror打印
    //   2. 打印创建成功的信息
    NamedPipe(const std::string &path, int who)
        : _fifo_path(path), _id(who), _fd(DefaultFd)
    {
        if (_id == Creator)
        {
            int ret = mkfifo(_fifo_path.c_str(), 0666);
            if (ret != 0)
            {
                perror("mkfifo");
            }
            std::cout << "creator create named pipe" << std::endl;
        }
    }

    // 只是为了重构函数后，让用户在调用的时候能更简便
    bool openForRead()
    {
        return openNamedPipe(Read);
    }

    bool openForWrite()
    {
        return openNamedPipe(Write);
    }

    // readNamedPipe 这个成员函数的功能
    //  1. 从_fifo_path.c_str()这个名称的文件中读取一串字符，把这串字符放到out中
    //     注意：想要往一个文件中读内容，
    //           可以通过ssize_t read(int fd, void *buf, size_t count); 从文件描述符中读
    //     对于 read() 函数的说明：
    //          1. read()会把参数fd所指的文件传送count 个字节到buf 指针所指的内存中
    //          2. 返回值是实际放到 buf 中的字节数，此返回值可能小于 count
    //  2. 将read()函数调用之后得到的字符数返回
    int readNamedPipe(std::string *out) // 这里之所以用指针，完全是因为想要表明这个参数是输出型参数
    {
        char buf[BaseSize] = {0};
        int n = read(_fd, buf, sizeof(buf));
        if (n > 0)
        {
            buf[n] = 0;
            *out = buf;
        }
        return n;
    }

    // writeNamedPipe 这个成员函数的功能
    //  1. 向 _fifo_path.c_str() 这个字符串中写入 in 这个字符串
    //     注意：想要往一个文件中写内容，
    //           可以通过ssize_t write(int fd, const void *buf, size_t count); 往文件描述符中写
    //     对于 write() 函数的说明：
    //          1. write() 会把参数buf所指的内存写入count个字节到参数fd所指的文件内
    //          2. 如果write写入顺利，则会返回实际写入的字节数
    //             如果写入失败，则返回-1
    //  2. 将写入的字节数返回
    int writeNamedPipe(const std::string &in) // 这里之所以用const修饰的引用，完全是因为想要表明这个参数是输入型参数
    {
        return write(_fd, in.c_str(), in.size());
    }

    // 析构函数的功能，
    // 1. 如果调用析构函数的对象的用户的身份是creator, 才将创建的管道文件删除
    // 2. 无论用户的身份是什么，只创建了一个openNamedPipe类型的对象，
    //    都需要调用 open 函数打开_fifo_path.c_str()这个位置的文件
    //    那么调用了open，都需要使用close关闭该文件
    ~NamedPipe()
    {
        if (_id == Creator)
        {
            int ret = unlink(_fifo_path.c_str());
            if (ret == -1)
            {
                perror("unlink");
            }
            std::cout << "creator free named pipe" << std::endl;
        }

        if (_fd != DefaultFd)
        {
            close(_fd);
        }
    }

private:
    const std::string _fifo_path;
    // _id是为了区分当前NamePipe类型的对象是为了
    int _id;
    int _fd;
};
